/* 
  编码日期：2021.9.5
  BY : 肖东 & 以鼎（北京）科技有限公司
  码容：汇顶蓝牙OTA批量升级demo
  备注：使用最好基于ble5.0 4.2及以下升级速度增加一倍，实测PC4.2 10几秒 RPI4 5.0 仅需5秒
  ota_type 用于控制常规升级还是fast升级模式
  可用采用WEB调用方式进行批量导入进行批量升级。目前版本未实现。
  setsockopt 可设MTU等参数本版本暂未实现
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h> 
#include <unistd.h>
#include <signal.h>

#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
#include <bluetooth/l2cap.h>

#include "dfu_master.h"
#include "gr5513_ota_m.h"
#include "user_dfu_m_cfg.h"
#include "minimal-ws-server-pmd.h"
#include "ws-server-cmd-sch.h"

#ifndef RECEIVE_MAX_LEN
#define RECEIVE_MAX_LEN 2048
#endif

//#define BDADDR_MYBLE   (&(bdaddr_t) {{0xb4,0x6b,0xfc,0xd8,0x8e,0x29}})
//#define BDADDR_MYBLE (&(bdaddr_t){{0x29, 0x8e, 0xd8, 0xfc, 0x6b, 0xb4}}) 
//#define BDADDR_MYBLE (&(bdaddr_t){{0x00, 0x85, 0x23, 0x8b, 0x31, 0x1d}}) ////1D:31:8B:23:85:00
#define test_BDADDR_REBLE (&(bdaddr_t){{0x00, 0x85, 0x23, 0x86, 0x08, 0x1d}})
//#define BDADDR_REBLE (&(bdaddr_t){{0x00, 0x85, 0x23, 0x90, 0x48, 0x1d}})
//#define BDADDR_REBLE (&(bdaddr_t){{0x85, 0x23, 0x4d, 0x90, 0x48, 0x1d}})
#define M_s_once_size  244

#define ATT_CID 4
#define MYQUET 5050

#ifdef  test_cmd
char buf[128] = {0x52,0x11,0x00,0x44,0x47,0x23,0x00,0x29,0x00,0x00,0x44,0x47,0x01,0x00,0xc0,0x9d,
                                 0x01,0x00,0x47,0x6e,0xa1,0x00,0x00,0x00,0x04,0x01,0x00,0x20,0x00,0x01,0x0b,0x00,
                                 0x00,0x00,0x44,0x03,0x00,0x00,0x62,0x6c,0x65,0x5f,0x61,0x70,0x70,0x5f,0x62,0x65,
                                 0x61,0x63,0xc1,0x08};//FW INFO
//5211004447230029000044470100c09d0100476ea10000000401002000010b00000044030000626c655f6170705f62656163c108
#endif
char  mtu[3]   = {0x02,0xfe,0x00};

uint8_t  cmd_req[5]   = {0x12,0x0f,0x00,0x01,0x00};
uint8_t  cmd_erase_internal[6]   = {0x44,0x4f,0x4f,0x47,0x05,0x00};//mcu  flash 类型(内部外部)

uint8_t  read_buf[255] = {0};
uint8_t write_buf[RECEIVE_MAX_LEN] ={0};

struct hci_dev_info di;

static pthread_t ReadSendClientSocketId, BleDfuId,HttpOtaId,WebSocketBLE;
volatile int signal_received = 0 ;
int sk = NULL;
int sock;
fd_set read_fds;
static uint16_t         s_master_curr_count;
bool ota_type = ota_nor;  //默认常规方式
bool ota_end_status = 0;

static void ble_sigint_handler(int sig)
{
    signal_received = sig;
    //printf("Ctrl+c 发送成功！！！！\n");
    //printf("signal_received = %d\n", signal_received);
}

void printids(const char *s)  
{  
        pid_t pid;  
        pthread_t tid;  
        pid = getpid();  
        tid = pthread_self();  
        printf("%s pid %u tid %u (0x%x)\n",s,(unsigned int)pid,  
                       (unsigned int)tid,(unsigned int)tid);  
}

void app_dfu_rev_cmd_cb(void)
{
    s_master_curr_count++;
}

void app_dfu_ota_end(bool status)
{
    printf(" ota_end_status = %d\r\n\r\n",status);
    ota_end_status = status;
}

void ble_socket_send_data(uint8_t *data, uint16_t len , uint16_t Handle)
{
    static int send_flags = -1;
    static uint8_t times, status;
    static uint16_t s_sended_len;

    //printf("%s\n", __func__);
    //memset(write_buf, 0, sizeof(write_buf));
    //memcpy(&write_buf,data,len);
    if (sk == NULL)
        return; //socket fd is NULL

    if (len >= M_s_once_size)
    {
        times = 0;
        status = 1;
        do
        {
            if (((times + 1) * M_s_once_size) < len)
            {
                memset(write_buf, 0, sizeof(write_buf));
                write_buf[0] = ATT_WR_CMD;
                write_buf[1] = Handle;
                write_buf[2] = Handle >> 8;
                memcpy(write_buf+3, (data + times * M_s_once_size), M_s_once_size);
                send_flags = send(sk, write_buf, M_s_once_size+3, 0);
  /*               printf("write_buf [%d] = [ ",times);
                for (uint16_t i = 0; i < M_s_once_size + 3; i++)
                    printf("%02x ", (uint8_t)write_buf[i]);
                printf(" ] send_flags = %d\r\n",send_flags); */
            }
            else
            {
                memset(write_buf, 0, sizeof(write_buf));
                write_buf[0] = ATT_WR_CMD;
                write_buf[1] = Handle;
                write_buf[2] = Handle >> 8;
                s_sended_len = len - M_s_once_size * times;
                memcpy(write_buf+3, (data + times * M_s_once_size), s_sended_len);
                send_flags = send(sk, write_buf, s_sended_len+3, 0);
                status = 0;
/*                 printf("write_buf [%d] = [ ",times);
                for (uint16_t i = 0; i < s_sended_len + 3; i++)
                    printf("%02x ", (uint8_t)write_buf[i]);
                printf(" ] send_flags = %d\r\n",send_flags); */
            }
            times++;
        } while (status);
    }
    else
    {
        memset(write_buf, 0, sizeof(write_buf));
        write_buf[0] = ATT_WR_CMD;
        write_buf[1] = Handle;
        write_buf[2] = Handle >> 8;
        memcpy(write_buf+3,data,len+3);
        send_flags = send(sk, write_buf, len+3, 0);
/*         printf("write_buf [%d] = [ ", times);
        for (uint16_t i = 0; i < len+3; i++)
            printf("%02x ", (uint8_t)write_buf[i]);
        printf(" ] send_flags = %d\r\n",send_flags); */
    }
    //printf("send  send_flags = %d ok\r",send_flags);
}

int  ble_socket_connect(bdaddr_t *BDADDR_REBLE,uint16_t timeout)
{
    struct sockaddr_l2 local_addr;
    struct sockaddr_l2 remote_addr;
    struct bt_security btsec;
    int sec = BT_SECURITY_LOW;
    uint8_t dst_type = BDADDR_LE_PUBLIC;
    int iRel_main = 0;
    static char ws_log[1024];

    sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
    if (sk < 0)
    {
        perror("\nsocket():");
    }

    printf("sk = %d remoter mac =  %02x:%02x:%02x:%02x:%02x:%02x\n", sk , BDADDR_REBLE->b[5], BDADDR_REBLE->b[4], BDADDR_REBLE->b[3],
           BDADDR_REBLE->b[2], BDADDR_REBLE->b[1], BDADDR_REBLE->b[0]);
    memset(ws_log,0,1024);
    sprintf(ws_log,"sk = %d remoter mac =  %02x:%02x:%02x:%02x:%02x:%02x\n", sk , BDADDR_REBLE->b[5], BDADDR_REBLE->b[4], BDADDR_REBLE->b[3],
           BDADDR_REBLE->b[2], BDADDR_REBLE->b[1], BDADDR_REBLE->b[0]);
    send_data_to_ws(ws_log,strlen(ws_log),2);
    memset(&local_addr, 0, sizeof(local_addr));
    local_addr.l2_family = AF_BLUETOOTH;
    local_addr.l2_cid = htobs(ATT_CID);
    local_addr.l2_bdaddr_type = 0;
    bacpy(&local_addr.l2_bdaddr, &di.bdaddr.b[0]);

    iRel_main = bind(sk, (struct sockaddr *)&local_addr, sizeof(struct sockaddr));
    if (iRel_main < 0)
    {
        perror("\nbind()");
        return -1;
    }
    /* Set the security level */
    memset(&btsec, 0, sizeof(btsec));
    btsec.level = sec;
    if (setsockopt(sk, SOL_BLUETOOTH, BT_SECURITY, &btsec,
                   sizeof(btsec)) != 0)
    {
        fprintf(stderr, "Failed to set L2CAP security level\n");
        close(sk);
        return -1;
    }

    struct l2cap_options l2o;
    socklen_t len;

    memset(&l2o, 0, sizeof(l2o));
    len = sizeof(l2o);
    if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0)
    {
        printf("getsockopt(L2CAP_OPTIONS) err \n");
        return -1;
    }
    l2o.imtu = 255;
    l2o.omtu = 1024;

    if (setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, sizeof(l2o)) < 0)
    {
        printf("setsockopt(L2CAP_OPTIONS) err \n");
        return -1;
    }

		struct timeval timeval;
		timeval.tv_sec = timeout;
		timeval.tv_usec = 0;

		if (setsockopt (sk, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeval, sizeof(timeval)) < 0) { //设置超时接收  connect 10秒超时退出
			printf("l2cap_connect: Failed to setsockopt for receive timeout.\n");
			return -1;
		}

		if (setsockopt (sk, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeval, sizeof(timeval)) < 0) { //设置超时发送  connect 10秒超时退出
			printf("l2cap_connect: Failed to setsockopt for sending timeout.\n");
			return -1;
		}

    memset(&remote_addr, 0, sizeof(remote_addr));
    remote_addr.l2_family = AF_BLUETOOTH;
    remote_addr.l2_cid = htobs(ATT_CID);
    remote_addr.l2_bdaddr_type = dst_type;
    remote_addr.l2_psm = htobs(0);
    bacpy(&remote_addr.l2_bdaddr, BDADDR_REBLE);


    if (connect(sk, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr_l2)))
    {
        close(sk);
        return -1;
    }

    printf("connected to remoter =  %02x:%02x:%02x:%02x:%02x:%02x\n",BDADDR_REBLE->b[5],BDADDR_REBLE->b[4],BDADDR_REBLE->b[3],
                                                            BDADDR_REBLE->b[2],BDADDR_REBLE->b[1],BDADDR_REBLE->b[0]);
    memset(ws_log,0,1024);
    sprintf(ws_log,"connected to remoter =  %02x:%02x:%02x:%02x:%02x:%02x\n",BDADDR_REBLE->b[5],BDADDR_REBLE->b[4],BDADDR_REBLE->b[3],
                                                            BDADDR_REBLE->b[2],BDADDR_REBLE->b[1],BDADDR_REBLE->b[0]);
    send_data_to_ws(ws_log,strlen(ws_log),0);
    return 1;
}

static void *ble_dtu_sch(void *arg) //ble 扫描
{
    printids(__func__);
    
    usleep(100);
    while (1)
    {

        if (signal_received == SIGINT || signal_received == MYQUET)
        {
            goto done;
        }
        if (!ota_type)
        {
            dfu_m_schedule(app_dfu_rev_cmd_cb);
        }
        else{
            dfu_m_fast_schedule(app_dfu_rev_cmd_cb);
        }
        usleep(1000);
    }
done:
    signal_received = MYQUET;
    printf("ble_dtu_sch exited!!\n");
    return NULL;
}

static void *TcpMangerClient(void *arg) //socket 接受发射
{
    static int ret = -1;
    struct timeval tv_timeout;
    static u_int16_t counter = 0;
    static uint16_t *fast_cmd_handler;

    printids(__func__);
    while (1)
    {
        if (signal_received == SIGINT || signal_received == MYQUET)
        {
            printf("recv signal_received %d \n", signal_received);
            goto done;
        }
        //if sk
        FD_ZERO(&read_fds);
        FD_SET(sk, &read_fds);

        tv_timeout.tv_sec = 0;
        tv_timeout.tv_usec = 5 * 1000;
        counter++;
        memset(read_buf,0,255);
        switch (select(sk + 1, &read_fds, NULL, NULL, &tv_timeout))
        {
        case -1:
            //printf("select error\n");
            break;
        case 0:
            //printf("select timeout\n");
            break;
        default:
            if (1)
            {
                //读取数据
                ret = read(sk, read_buf, 255);
                //printf("read len = %d\n",ret);
            }
            break;
        }
        if (ret > 0)
        {
            /*printf("recv len %d data = ", ret);
            for (uint16_t i = 0; i < ret; i++)
                printf("%02x ", (uint8_t)read_buf[i]);
            printf("\r\n");*/
            switch (ota_type)
            {
            case ota_nor:
                if (ret > 9 && read_buf[3] == 0x44 && read_buf[4] == 0x47)
                {
                    dfu_m_cmd_prase(read_buf + 3, ret - 3); //hci_cmd+handle+data
                }
                ret = -1;
                break;
            case ota_fast:

                fast_cmd_handler = &read_buf[1];
                if (*fast_cmd_handler == DFU_fast_cmd_handler)
                {
                    dfu_fast_cmd_prase(read_buf + 3, ret - 3); //hci_cmd+handle+data
                }
                ret = -1;
                break;
    
            default:
                break;
            }
        }
        usleep(5000);
        if(counter >= 1000)
        {
            //printf("TcpMangerClient ret = %d\n",ret);
            counter = 0;
        }
    }
done:
    signal_received = MYQUET;
    printf("TcpMangerClient exited!!\n");
    return NULL;
}


static void *ble_dtu_http_sch(void *arg)
{
    FILE *ble_mac_fd,*ble_ota_fail,*ble_run_log;
	struct l2cap_conninfo conninfo;
	socklen_t socklen;
    static char temp_ADDR[26];
    static char ws_buff[1024];
    static bdaddr_t temp_mac;
    int iRel = 0;
    int size = 0;
    int socket_status = 0;
    uint16_t counter_time = 0;
    printids(__func__);
    ble_run_log = fopen("data/ble_run.log", "wr+");
    ble_mac_fd = fopen(http_dev_mac_config, "r");
	if (ble_mac_fd ==NULL)
	{
		printf("没有HTTP conf 文件\n");
        goto done;
	}
    sprintf(temp_ADDR,"> %s",http_ble_fail); //清空文本
    system(temp_ADDR);
    pthread_create(&ReadSendClientSocketId, NULL, TcpMangerClient, NULL);  //创建蓝牙服务接收thread
    pthread_create(&BleDfuId, NULL, ble_dtu_sch, NULL);  
    sleep(1);
    memset(temp_ADDR,0,sizeof(temp_ADDR));
    fgets(temp_ADDR, sizeof(temp_ADDR), ble_mac_fd);
    printf("%s",temp_ADDR);
    ota_type = strstr(temp_ADDR,"ota_type=nor") ? ota_nor : ota_fast;
    printf("ota_type =%d\n",ota_type);
    memset(temp_ADDR,0,sizeof(temp_ADDR));
    while (fgets(temp_ADDR, sizeof(temp_ADDR), ble_mac_fd) != NULL)
    {
        //printf("%s len = %d counter_time = %d \n",temp_ADDR,strlen(temp_ADDR),counter_time);
        if (signal_received == SIGINT || signal_received == MYQUET)
        {
            goto done;
        }
        if(strlen(temp_ADDR) == 19){
            temp_ADDR[18] = NULL; // 消除windows下的文本回车或者换行
        }
        if(strlen(temp_ADDR) == 18)
        {
            temp_ADDR[17] = NULL; // 消除回车或者换行
            //printf("str2ba = %d\n",str2ba(temp_ADDR,&temp_mac));
            str2ba(temp_ADDR,&temp_mac);
            counter_time = 0;
            do{
                //re connect
                counter_time++;
                sleep(1);
                socket_status = ble_socket_connect(&temp_mac,5);
                if(counter_time >= 2)
                {
                    printf("ble_socket_connect err =%d\n",counter_time);
                    ble_ota_fail = fopen(http_ble_fail, "r+");
                    fseek(ble_ota_fail,0L,SEEK_END);
                    temp_ADDR[17] = '\n'; // 消除回车或者换行
                    printf("err %s\n",temp_ADDR);
                    fputs(temp_ADDR,ble_ota_fail); //将升级失败的记录在文件中
                    fclose(ble_ota_fail);
                    memset(ws_buff,0,sizeof(ws_buff));
                    sprintf(ws_buff,"%s",temp_ADDR);
                    send_data_to_ws(ws_buff,strlen(ws_buff),1);
                    goto next_dev;   
                }
                if(socket_status < 0) close(sk); //断开连接

            }while(socket_status < 0);

            iRel = send(sk, mtu, 3, 0); //更改MTU大小
            printf(" change mtu=%d\n", (uint8_t)mtu[1]);
            sleep(1);
            iRel = send(sk, cmd_req, 5, 0); // 设置服务回显
            printf(" cmd req=%d\n", cmd_req[1]);
            usleep(10000);
            memset(read_buf, 0, 255);

            user_dfu_m_init(DFU_DATA_SEND_SIZE);
            if (!ota_type)
            {
                dfu_m_program_start(0, 1); // no  encrypted  ota end and reset
            }
            else
            {
                ble_socket_send_data(cmd_erase_internal, sizeof(cmd_erase_internal), DFU_fast_cmd_handler);
            }

            counter_time = 0;  //等待升级结束
            do{
                usleep(20000);
                counter_time++;
                if(counter_time == 50*20) goto next_dev;
            }while(ota_end_status == 0);
            fprintf(ble_run_log,"%s status= %d\n",temp_ADDR,ota_end_status);
next_dev: //新的ble设备
            socklen = sizeof(conninfo);
            socket_status = getsockopt(sk, SOL_L2CAP, L2CAP_CONNINFO, &conninfo, &socklen) ;
            printf(" getsockopt status = %d hci handle = 0x%X \n", socket_status ,conninfo.hci_handle);
            //hci_disconnect(sock, conninfo.hci_handle, HCI_OE_USER_ENDED_CONNECTION, 3000);
            //printf("hci_disconnect status = %d \n",hci_disconnect(sock, conninfo.hci_handle, HCI_OE_USER_ENDED_CONNECTION, 10000));
            close(sk); //断开连接
            counter_time = 1;
            dfu_m_parse_state_reset();
        }
        usleep(10000);
        memset(temp_ADDR,0,sizeof(temp_ADDR));
    }
done_err:
    printf("ble_dtu_http_sch over time!!\n");
done:
    if(ble_mac_fd != NULL) fclose(ble_mac_fd);
    if(ble_run_log != NULL) fclose(ble_run_log);
    signal_received = MYQUET;
    printf("ble_dtu_http_sch!!\n");
    return NULL;
}

void app_ota_start(void)
{
    signal_received = 0;
    pthread_create(&HttpOtaId, NULL, ble_dtu_http_sch, NULL);              //创建蓝牙OTA批量任务服务thread
}


int main(int argc, char **argv)
{

    int max_rsp, num_rsp;
    int dev_id;
    uid_t uid, euid;
    uid = getuid();
    euid = geteuid();
    if (setreuid(euid, uid)) //
    {
        printf("setreuid");
    }
    printf("euid = %d uid = %d\r\n", euid, uid);
    printids(__func__);
    printf("APP build time %s %s \r\n",__DATE__,__TIME__);
     dev_id = hci_get_route(NULL);
     sock = hci_open_dev( dev_id );
     if (dev_id < 0 || sock < 0) {
         printf("opening sock");
         exit(1);
     }

	if (hci_devinfo(dev_id, &di) < 0) {
		printf("Can't get device info for hci\n");
		hci_close_dev(sock);
		exit(1);
	}
    hci_close_dev(sock);

    signal(SIGINT, ble_sigint_handler);

    printf("dev id = hci%d sock = %d host ble mac =  %02x:%02x:%02x:%02x:%02x:%02x\n",dev_id,sock,di.bdaddr.b[5],di.bdaddr.b[4],di.bdaddr.b[3],
                                                                                di.bdaddr.b[2],di.bdaddr.b[1],di.bdaddr.b[0]);
       
    printf("\nargv[0]:%s  argv[1]:%s\n", argv[0], argv[1]);


    //ble_socket_connect(test_BDADDR_REBLE,5);
    printf(" send data.....................................................................\n");

    sleep(3);                  //创建蓝牙OTA服务thread
    pthread_create(&WebSocketBLE, NULL, ws_main, NULL);              //创建HTTP和websocket服务
done:
    pthread_join(WebSocketBLE, NULL);
    send_data_to_ws("exit\n",6,3);
    printf("WebSocketBLE close !\r\n");
    pthread_join(HttpOtaId, NULL);
    printf("HttpOtaId close !\r\n");
    pthread_join(ReadSendClientSocketId, NULL);
    printf("ReadSendClientSocketId close !\r\n");
    pthread_join(BleDfuId, NULL);
    printf("BleDfuId close ! socket close = %d\r\n", close(sk));
    return NULL;
}
